// Decompiled on 周六 2月 22 19:31:49 CST 2025 with Zomboid Decompiler v0.1.3 using Vineflower.
package zombie;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URI;
import java.nio.file.DirectoryStream;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.DirectoryStream.Filter;
import java.security.AccessControlException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import zombie.Lua.LuaEventManager;
import zombie.core.Core;
import zombie.core.logger.ExceptionLogger;
import zombie.core.znet.SteamUtils;
import zombie.core.znet.SteamWorkshop;
import zombie.debug.DebugLog;
import zombie.debug.LogSeverity;
import zombie.gameStates.ChooseGameInfo;
import zombie.iso.IsoWorld;
import zombie.iso.sprite.IsoSpriteManager;
import zombie.modding.ActiveMods;
import zombie.modding.ActiveModsFile;
import zombie.network.CoopMaster;
import zombie.network.GameClient;
import zombie.network.GameServer;
import zombie.util.StringUtils;

public final class ZomboidFileSystem {
    public static final zombie.ZomboidFileSystem instance = new zombie.ZomboidFileSystem();
    private final ArrayList<String> loadList = new ArrayList();
    private final Map<String, String> modIdToDir = new HashMap();
    private final Map<String, ChooseGameInfo.Mod> modDirToMod = new HashMap();
    private ArrayList<String> modFolders;
    private ArrayList<String> modFoldersOrder;
    public final HashMap<String, String> ActiveFileMap = new HashMap();
    private final HashSet<String> AllAbsolutePaths = new HashSet();
    public File base;
    public URI baseURI;
    private File workdir;
    private URI workdirURI;
    private File localWorkdir;
    private File anims;
    private URI animsURI;
    private File animsX;
    private URI animsXURI;
    private File animSets;
    private URI animSetsURI;
    private File actiongroups;
    private URI actiongroupsURI;
    private File cacheDir;
    private final ConcurrentHashMap<String, String> RelativeMap = new ConcurrentHashMap();
    public final ThreadLocal<Boolean> IgnoreActiveFileMap = ThreadLocal.withInitial(() -> Boolean.FALSE);
    private final ConcurrentHashMap<String, URI> CanonicalURIMap = new ConcurrentHashMap();
    private final ArrayList<String> mods = new ArrayList();
    private final HashSet<String> LoadedPacks = new HashSet();
    private zombie.FileGuidTable m_fileGuidTable = null;
    private boolean m_fileGuidTableWatcherActive = false;
    private final zombie.PredicatedFileWatcher m_modFileWatcher = new zombie.PredicatedFileWatcher(this::isModFile, this::onModFileChanged);
    private final HashSet<String> m_watchedModFolders = new HashSet();
    private long m_modsChangedTime = 0L;

    private ZomboidFileSystem() {
    }

    public void init() throws IOException {
        this.base = new File("./").getAbsoluteFile().getCanonicalFile();
        this.baseURI = this.base.toURI();
        this.workdir = new File(this.base, "media").getAbsoluteFile().getCanonicalFile();
        this.workdirURI = this.workdir.toURI();
        this.localWorkdir = this.base.toPath().relativize(this.workdir.toPath()).toFile();
        this.anims = new File(this.workdir, "anims");
        this.animsURI = this.anims.toURI();
        this.animsX = new File(this.workdir, "anims_X");
        this.animsXURI = this.animsX.toURI();
        this.animSets = new File(this.workdir, "AnimSets");
        this.animSetsURI = this.animSets.toURI();
        this.actiongroups = new File(this.workdir, "actiongroups");
        this.actiongroupsURI = this.actiongroups.toURI();
        this.searchFolders(this.workdir);

        for (int _int = 0; _int < this.loadList.size(); _int++) {
            String string0 = this.getRelativeFile((String)this.loadList.get(_int));
            File file = new File((String)this.loadList.get(_int)).getAbsoluteFile();
            String string1 = file.getAbsolutePath();
            if (file.isDirectory()) {
                string1 = string1 + File.separator;
            }

            this.ActiveFileMap.put(string0.toLowerCase(Locale.ENGLISH), string1);
            this.AllAbsolutePaths.add(string1);
        }

        this.loadList.clear();
    }

    public File getCanonicalFile(File file0, String string) {
        if (!file0.isDirectory()) {
            return new File(file0, string);
        } else {
            File[] file1 = file0.listFiles((var1x, string0) -> string0.equalsIgnoreCase(string));
            return file1 != null && file1.length != 0 ? file1[0] : new File(file0, string);
        }
    }

    public String getGameModeCacheDir() {
        if (Core.GameMode == null) {
            Core.GameMode = "Sandbox";
        }

        String string = this.getSaveDir();
        return string + File.separator + Core.GameMode;
    }

    public String getCurrentSaveDir() {
        return this.getGameModeCacheDir() + File.separator + Core.GameSaveWorld;
    }

    public String getFileNameInCurrentSave(String fileName) {
        return this.getCurrentSaveDir() + File.separator + fileName;
    }

    public String getFileNameInCurrentSave(String subDir, String fileName) {
        return this.getFileNameInCurrentSave(subDir + File.separator + fileName);
    }

    public String getFileNameInCurrentSave(String subDir1, String subDir2, String fileName) {
        return this.getFileNameInCurrentSave(subDir1 + File.separator + subDir2 + File.separator + fileName);
    }

    public File getFileInCurrentSave(String fileName) {
        return new File(this.getFileNameInCurrentSave(fileName));
    }

    public File getFileInCurrentSave(String subDir, String fileName) {
        return new File(this.getFileNameInCurrentSave(subDir, fileName));
    }

    public File getFileInCurrentSave(String subDir1, String subDir2, String fileName) {
        return new File(this.getFileNameInCurrentSave(subDir1, subDir2, fileName));
    }

    public String getSaveDir() {
        String string = this.getCacheDirSub("Saves");
        ensureFolderExists(string);
        return string;
    }

    public String getSaveDirSub(String subPath) {
        return this.getSaveDir() + File.separator + subPath;
    }

    public String getScreenshotDir() {
        String string = this.getCacheDirSub("Screenshots");
        ensureFolderExists(string);
        return string;
    }

    public String getScreenshotDirSub(String subPath) {
        return this.getScreenshotDir() + File.separator + subPath;
    }

    public void setCacheDir(String dir) {
        dir = dir.replace("/", File.separator);
        this.cacheDir = new File(dir).getAbsoluteFile();
        ensureFolderExists(this.cacheDir);
    }

    public String getCacheDir() {
        if (this.cacheDir == null) {
            String string0 = System.getProperty("deployment.user.cachedir");
            if (string0 == null || System.getProperty("os.name").startsWith("Win")) {
                string0 = System.getProperty("user.home");
            }

            String string1 = string0 + File.separator + "Zomboid";
            this.setCacheDir(string1);
        }

        return this.cacheDir.getPath();
    }

    public String getCacheDirSub(String subPath) {
        return this.getCacheDir() + File.separator + subPath;
    }

    public String getMessagingDir() {
        String string = this.getCacheDirSub("messaging");
        ensureFolderExists(string);
        return string;
    }

    public String getMessagingDirSub(String subPath) {
        return this.getMessagingDir() + File.separator + subPath;
    }

    public File getMediaRootFile() {
        assert this.workdir != null;

        return this.workdir;
    }

    public String getMediaRootPath() {
        return this.workdir.getPath();
    }

    public File getMediaFile(String subPath) {
        assert this.workdir != null;

        return new File(this.workdir, subPath);
    }

    public String getMediaPath(String subPath) {
        return this.getMediaFile(subPath).getPath();
    }

    public String getAbsoluteWorkDir() {
        return this.workdir.getPath();
    }

    public String getLocalWorkDir() {
        return this.localWorkdir.getPath();
    }

    public String getLocalWorkDirSub(String subPath) {
        return this.getLocalWorkDir() + File.separator + subPath;
    }

    public String getAnimSetsPath() {
        return this.animSets.getPath();
    }

    public String getActionGroupsPath() {
        return this.actiongroups.getPath();
    }

    public static boolean ensureFolderExists(String path) {
        return ensureFolderExists(new File(path).getAbsoluteFile());
    }

    public static boolean ensureFolderExists(File directory) {
        return directory.exists() || directory.mkdirs();
    }

    public void searchFolders(File fo) {
        if (!GameServer.bServer) {
            Thread.yield();
            Core.getInstance().DoFrameReady();
        }

        if (fo.isDirectory()) {
            String string0 = fo.getAbsolutePath().replace("\\", "/").replace("./", "");
            if (string0.contains("media/maps/")) {
                this.loadList.add(string0);
            }

            String[] string1 = fo.list();

            for (int _int = 0; _int < string1.length; _int++) {
                this.searchFolders(new File(fo.getAbsolutePath() + File.separator + string1[_int]));
            }
        } else {
            this.loadList.add(fo.getAbsolutePath().replace("\\", "/").replace("./", ""));
        }
    }

    public Object[] getAllPathsContaining(String str) {
        ArrayList arrayList = new ArrayList();

        for (Entry entry : this.ActiveFileMap.entrySet()) {
            if (((String)entry.getKey()).contains(str)) {
                arrayList.add((String)entry.getValue());
            }
        }

        return arrayList.toArray();
    }

    public Object[] getAllPathsContaining(String str, String str2) {
        ArrayList arrayList = new ArrayList();

        for (Entry entry : this.ActiveFileMap.entrySet()) {
            if (((String)entry.getKey()).contains(str) && ((String)entry.getKey()).contains(str2)) {
                arrayList.add((String)entry.getValue());
            }
        }

        return arrayList.toArray();
    }

    public synchronized String getString(String str) {
        if ((Boolean)this.IgnoreActiveFileMap.get()) {
            return str;
        } else {
            String string0 = str.toLowerCase(Locale.ENGLISH);
            String string1 = (String)this.RelativeMap.get(string0);
            if (string1 != null) {
                string0 = string1;
            } else {
                String string2 = this.getRelativeFile(str);
                string0 = string2.toLowerCase(Locale.ENGLISH);
                this.RelativeMap.put(string0, string0);
            }

            String string3 = (String)this.ActiveFileMap.get(string0);
            return string3 != null ? string3 : str;
        }
    }

    public synchronized boolean isKnownFile(String string0) {
        if (this.AllAbsolutePaths.contains(string0)) {
            return true;
        } else {
            String string1 = string0.toLowerCase(Locale.ENGLISH);
            String string2 = (String)this.RelativeMap.get(string1);
            if (string2 != null) {
                string1 = string2;
            } else {
                String string3 = this.getRelativeFile(string0);
                string1 = string3.toLowerCase(Locale.ENGLISH);
                this.RelativeMap.put(string1, string1);
            }

            String string4 = (String)this.ActiveFileMap.get(string1);
            return string4 != null;
        }
    }

    public String getAbsolutePath(String rel) {
        String string = rel.toLowerCase(Locale.ENGLISH);
        return (String)this.ActiveFileMap.get(string);
    }

    public void Reset() {
        this.loadList.clear();
        this.ActiveFileMap.clear();
        this.AllAbsolutePaths.clear();
        this.CanonicalURIMap.clear();
        this.modIdToDir.clear();
        this.modDirToMod.clear();
        this.mods.clear();
        this.modFolders = null;
        ActiveMods.Reset();
        if (this.m_fileGuidTable != null) {
            this.m_fileGuidTable.clear();
            this.m_fileGuidTable = null;
        }
    }

    public File getCanonicalFile(File file) {
        try {
            return file.getCanonicalFile();
        } catch (Exception exception) {
            return file.getAbsoluteFile();
        }
    }

    public File getCanonicalFile(String path) {
        return this.getCanonicalFile(new File(path));
    }

    public String getCanonicalPath(File file) {
        try {
            return file.getCanonicalPath();
        } catch (Exception exception) {
            return file.getAbsolutePath();
        }
    }

    public String getCanonicalPath(String path) {
        return this.getCanonicalPath(new File(path));
    }

    public URI getCanonicalURI(String path) {
        URI uRI = (URI)this.CanonicalURIMap.get(path);
        if (uRI == null) {
            uRI = this.getCanonicalFile(path).toURI();
            this.CanonicalURIMap.put(path, uRI);
        }

        return uRI;
    }

    public void resetModFolders() {
        this.modFolders = null;
    }

    public void getInstalledItemModsFolders(ArrayList<String> out) {
        if (SteamUtils.isSteamModeEnabled()) {
            String[] string0 = SteamWorkshop.instance.GetInstalledItemFolders();
            if (string0 != null) {
                for (String string1 : string0) {
                    File file = new File(string1 + File.separator + "mods");
                    if (file.exists()) {
                        out.add(file.getAbsolutePath());
                    }
                }
            }
        }
    }

    public void getStagedItemModsFolders(ArrayList<String> out) {
        if (SteamUtils.isSteamModeEnabled()) {
            ArrayList arrayList = SteamWorkshop.instance.getStageFolders();

            for (int _int = 0; _int < arrayList.size(); _int++) {
                File file = new File((String)arrayList.get(_int) + File.separator + "Contents" + File.separator + "mods");
                if (file.exists()) {
                    out.add(file.getAbsolutePath());
                }
            }
        }
    }

    private void getAllModFoldersAux(String string0, List<String> list) {
        Filter filter = new Filter<Path>() {
            public boolean accept(Path path) throws IOException {
                return Files.isDirectory(path, new LinkOption[0]) && Files.exists(path.resolve("mod.info"), new LinkOption[0]);
            }
        };
        Path path0 = FileSystems.getDefault().getPath(string0);
        if (Files.exists(path0, new LinkOption[0])) {
            try {
                DirectoryStream directoryStream = Files.newDirectoryStream(path0, filter);

                try {
                    for (Path path1 : directoryStream) {
                        if (path1.getFileName().toString().toLowerCase().equals("examplemod")) {
                            DebugLog.Mod.println("refusing to list " + path1.getFileName());
                        } else {
                            String string1 = path1.toAbsolutePath().toString();
                            if (!this.m_watchedModFolders.contains(string1)) {
                                this.m_watchedModFolders.add(string1);
                                zombie.DebugFileWatcher.instance.addDirectory(string1);
                                Path path2 = path1.resolve("media");
                                if (Files.exists(path2, new LinkOption[0])) {
                                    zombie.DebugFileWatcher.instance.addDirectoryRecurse(path2.toAbsolutePath().toString());
                                }
                            }

                            list.add(string1);
                        }
                    }
                } catch (Throwable throwable0) {
                    if (directoryStream != null) {
                        try {
                            directoryStream.close();
                        } catch (Throwable throwable1) {
                            throwable0.addSuppressed(throwable1);
                        }
                    }

                    throw throwable0;
                }

                if (directoryStream != null) {
                    directoryStream.close();
                }
            } catch (Exception exception) {
                exception.printStackTrace();
            }
        }
    }

    public void setModFoldersOrder(String s) {
        this.modFoldersOrder = new ArrayList(Arrays.asList(s.split(",")));
    }

    public void getAllModFolders(List<String> out) {
        if (this.modFolders == null) {
            this.modFolders = new ArrayList();
            if (this.modFoldersOrder == null) {
                this.setModFoldersOrder("workshop,steam,mods");
            }

            ArrayList arrayList = new ArrayList();

            for (int int0 = 0; int0 < this.modFoldersOrder.size(); int0++) {
                String string0 = (String)this.modFoldersOrder.get(int0);
                if ("workshop".equals(string0)) {
                    this.getStagedItemModsFolders(arrayList);
                }

                if ("steam".equals(string0)) {
                    this.getInstalledItemModsFolders(arrayList);
                }

                if ("mods".equals(string0)) {
                    arrayList.add(Core.getMyDocumentFolder() + File.separator + "mods");
                }
            }

            for (int int1 = 0; int1 < arrayList.size(); int1++) {
                String string1 = (String)arrayList.get(int1);
                if (!this.m_watchedModFolders.contains(string1)) {
                    this.m_watchedModFolders.add(string1);
                    zombie.DebugFileWatcher.instance.addDirectory(string1);
                }

                this.getAllModFoldersAux(string1, this.modFolders);
            }

            zombie.DebugFileWatcher.instance.add(this.m_modFileWatcher);
        }

        out.clear();
        out.addAll(this.modFolders);
    }

    public ArrayList<ChooseGameInfo.Mod> getWorkshopItemMods(long itemID) {
        ArrayList arrayList = new ArrayList();
        if (!SteamUtils.isSteamModeEnabled()) {
            return arrayList;
        } else {
            String string = SteamWorkshop.instance.GetItemInstallFolder(itemID);
            if (string == null) {
                return arrayList;
            } else {
                File file0 = new File(string + File.separator + "mods");
                if (file0.exists() && file0.isDirectory()) {
                    File[] file1 = file0.listFiles();

                    for (File file2 : file1) {
                        if (file2.isDirectory()) {
                            ChooseGameInfo.Mod mod = ChooseGameInfo.readModInfo(file2.getAbsolutePath());
                            if (mod != null) {
                                arrayList.add(mod);
                            }
                        }
                    }

                    return arrayList;
                } else {
                    return arrayList;
                }
            }
        }
    }

    public ChooseGameInfo.Mod searchForModInfo(File path, String modSearched, ArrayList<ChooseGameInfo.Mod> _mods) {
        if (path.isDirectory()) {
            String[] string = path.list();
            if (string == null) {
                return null;
            }

            for (int _int = 0; _int < string.length; _int++) {
                File file = new File(path.getAbsolutePath() + File.separator + string[_int]);
                ChooseGameInfo.Mod mod0 = this.searchForModInfo(file, modSearched, _mods);
                if (mod0 != null) {
                    return mod0;
                }
            }
        } else if (path.getAbsolutePath().endsWith("mod.info")) {
            ChooseGameInfo.Mod mod1 = ChooseGameInfo.readModInfo(path.getAbsoluteFile().getParent());
            if (mod1 == null) {
                return null;
            }

            if (!StringUtils.isNullOrWhitespace(mod1.getId())) {
                this.modIdToDir.put(mod1.getId(), mod1.getDir());
                _mods.add(mod1);
            }

            if (mod1.getId().equals(modSearched)) {
                return mod1;
            }
        }

        return null;
    }

    public void loadMod(String path) {
        if (this.getModDir(path) != null) {
            if (CoopMaster.instance != null) {
                CoopMaster.instance.update();
            }

            DebugLog.Mod.println("loading " + path);
            File file = new File(this.getModDir(path));
            URI uRI = file.toURI();
            this.loadList.clear();
            this.searchFolders(file);

            for (int _int = 0; _int < this.loadList.size(); _int++) {
                String string0 = this.getRelativeFile(uRI, (String)this.loadList.get(_int));
                string0 = string0.toLowerCase(Locale.ENGLISH);
                if (this.ActiveFileMap.containsKey(string0) && !string0.endsWith("mod.info") && !string0.endsWith("poster.png")) {
                    DebugLog.Mod.println("mod \"" + path + "\" overrides " + string0);
                }

                String string1 = new File((String)this.loadList.get(_int)).getAbsolutePath();
                this.ActiveFileMap.put(string0, string1);
                this.AllAbsolutePaths.add(string1);
            }

            this.loadList.clear();
        }
    }

    private ArrayList<String> readLoadedDotTxt() {
        String string0 = Core.getMyDocumentFolder() + File.separator + "mods" + File.separator + "loaded.txt";
        File file = new File(string0);
        if (!file.exists()) {
            return null;
        } else {
            ArrayList arrayList = new ArrayList();

            try {
                FileReader fileReader = new FileReader(string0);

                try {
                    BufferedReader bufferedReader = new BufferedReader(fileReader);

                    try {
                        for (String string1 = bufferedReader.readLine(); string1 != null; string1 = bufferedReader.readLine()) {
                            string1 = string1.trim();
                            if (!string1.isEmpty()) {
                                arrayList.add(string1);
                            }
                        }
                    } catch (Throwable throwable0) {
                        try {
                            bufferedReader.close();
                        } catch (Throwable throwable1) {
                            throwable0.addSuppressed(throwable1);
                        }

                        throw throwable0;
                    }

                    bufferedReader.close();
                } catch (Throwable throwable2) {
                    try {
                        fileReader.close();
                    } catch (Throwable throwable3) {
                        throwable2.addSuppressed(throwable3);
                    }

                    throw throwable2;
                }

                fileReader.close();
            } catch (Exception exception0) {
                ExceptionLogger.logException(exception0);
                arrayList = null;
            }

            try {
                file.delete();
            } catch (Exception exception1) {
                ExceptionLogger.logException(exception1);
            }

            return arrayList;
        }
    }

    private ActiveMods readDefaultModsTxt() {
        ActiveMods activeMods = ActiveMods.getById("default");
        ArrayList arrayList = this.readLoadedDotTxt();
        if (arrayList != null) {
            activeMods.getMods().addAll(arrayList);
            this.saveModsFile();
        }

        activeMods.clear();
        String string = Core.getMyDocumentFolder() + File.separator + "mods" + File.separator + "default.txt";

        try {
            ActiveModsFile activeModsFile = new ActiveModsFile();
            if (activeModsFile.read(string, activeMods)) {
            }
        } catch (Exception exception) {
            ExceptionLogger.logException(exception);
        }

        return activeMods;
    }

    public void loadMods(String activeMods) {
        if (Core.OptionModsEnabled) {
            if (GameClient.bClient) {
                ArrayList arrayList = new ArrayList();
                this.loadTranslationMods(arrayList);
                arrayList.addAll(GameClient.instance.ServerMods);
                this.loadMods(arrayList);
            } else {
                ActiveMods _activeMods = ActiveMods.getById(activeMods);
                if (!"default".equalsIgnoreCase(activeMods)) {
                    ActiveMods.setLoadedMods(_activeMods);
                    this.loadMods(_activeMods.getMods());
                } else {
                    try {
                        _activeMods = this.readDefaultModsTxt();
                        _activeMods.checkMissingMods();
                        _activeMods.checkMissingMaps();
                        ActiveMods.setLoadedMods(_activeMods);
                        this.loadMods(_activeMods.getMods());
                    } catch (Exception exception) {
                        ExceptionLogger.logException(exception);
                    }
                }
            }
        }
    }

    private boolean isTranslationMod(String string0) {
        ChooseGameInfo.Mod mod = ChooseGameInfo.getAvailableModDetails(string0);
        if (mod == null) {
            return false;
        } else {
            boolean _boolean = false;
            File file = new File(mod.getDir());
            URI uRI = file.toURI();
            this.loadList.clear();
            this.searchFolders(file);

            for (int _int = 0; _int < this.loadList.size(); _int++) {
                String string1 = this.getRelativeFile(uRI, (String)this.loadList.get(_int));
                if (string1.endsWith(".lua")) {
                    return false;
                }

                if (string1.startsWith("media/maps/")) {
                    return false;
                }

                if (string1.startsWith("media/scripts/")) {
                    return false;
                }

                if (string1.startsWith("media/lua/")) {
                    if (!string1.startsWith("media/lua/shared/Translate/")) {
                        return false;
                    }

                    _boolean = true;
                }
            }

            this.loadList.clear();
            return _boolean;
        }
    }

    private void loadTranslationMods(ArrayList<String> arrayList1) {
        if (GameClient.bClient) {
            ActiveMods activeMods = this.readDefaultModsTxt();
            ArrayList arrayList0 = new ArrayList();
            if (this.loadModsAux(activeMods.getMods(), arrayList0) == null) {
                for (String string : arrayList0) {
                    if (this.isTranslationMod(string)) {
                        DebugLog.Mod.println("loading translation mod \"" + string + "\"");
                        if (!arrayList1.contains(string)) {
                            arrayList1.add(string);
                        }
                    }
                }
            }
        }
    }

    private String loadModAndRequired(String string0, ArrayList<String> arrayList) {
        if (string0.isEmpty()) {
            return null;
        } else if (string0.toLowerCase().equals("examplemod")) {
            DebugLog.Mod.warn("refusing to load " + string0);
            return null;
        } else if (arrayList.contains(string0)) {
            return null;
        } else {
            ChooseGameInfo.Mod mod = ChooseGameInfo.getAvailableModDetails(string0);
            if (mod == null) {
                if (GameServer.bServer) {
                    GameServer.ServerMods.remove(string0);
                }

                DebugLog.Mod.warn("required mod \"" + string0 + "\" not found");
                return string0;
            } else {
                if (mod.getRequire() != null) {
                    String string1 = this.loadModsAux(mod.getRequire(), arrayList);
                    if (string1 != null) {
                        return string1;
                    }
                }

                arrayList.add(string0);
                return null;
            }
        }
    }

    public String loadModsAux(ArrayList<String> toLoad, ArrayList<String> ordered) {
        for (String string0 : toLoad) {
            String string1 = this.loadModAndRequired(string0, ordered);
            if (string1 != null) {
                return string1;
            }
        }

        return null;
    }

    public void loadMods(ArrayList<String> toLoad) {
        this.mods.clear();

        for (String string0 : toLoad) {
            this.loadModAndRequired(string0, this.mods);
        }

        for (String string1 : this.mods) {
            this.loadMod(string1);
        }
    }

    public ArrayList<String> getModIDs() {
        return this.mods;
    }

    public String getModDir(String modId) {
        return (String)this.modIdToDir.get(modId);
    }

    public ChooseGameInfo.Mod getModInfoForDir(String modDir) {
        ChooseGameInfo.Mod mod = (ChooseGameInfo.Mod)this.modDirToMod.get(modDir);
        if (mod == null) {
            mod = new ChooseGameInfo.Mod(modDir);
            this.modDirToMod.put(modDir, mod);
        }

        return mod;
    }

    public String getRelativeFile(File file) {
        return this.getRelativeFile(this.baseURI, file.getAbsolutePath());
    }

    public String getRelativeFile(String string) {
        return this.getRelativeFile(this.baseURI, string);
    }

    public String getRelativeFile(URI root, File file) {
        return this.getRelativeFile(root, file.getAbsolutePath());
    }

    public String getRelativeFile(URI root, String string) {
        URI uRI0 = this.getCanonicalURI(string);
        URI uRI1 = this.getCanonicalURI(root.getPath()).relativize(uRI0);
        if (uRI1.equals(uRI0)) {
            return string;
        } else {
            String _string = uRI1.getPath();
            if (string.endsWith("/") && !_string.endsWith("/")) {
                _string = _string + "/";
            }

            return _string;
        }
    }

    public String getAnimName(URI mediaURI, File file) {
        String string0 = this.getRelativeFile(mediaURI, file);
        String string1 = string0.toLowerCase(Locale.ENGLISH);
        int _int = string1.lastIndexOf(46);
        if (_int > -1) {
            string1 = string1.substring(0, _int);
        }

        if (string1.startsWith("anims/")) {
            string1 = string1.substring("anims/".length());
        } else if (string1.startsWith("anims_x/")) {
            string1 = string1.substring("anims_x/".length());
        }

        return string1;
    }

    public String resolveRelativePath(String srcFilePath, String relativePath) {
        Path path0 = Paths.get(srcFilePath);
        Path path1 = path0.getParent();
        Path path2 = path1.resolve(relativePath);
        String string = path2.toString();
        return this.getRelativeFile(string);
    }

    public void saveModsFile() {
        try {
            ensureFolderExists(Core.getMyDocumentFolder() + File.separator + "mods");
            String string = Core.getMyDocumentFolder() + File.separator + "mods" + File.separator + "default.txt";
            ActiveModsFile activeModsFile = new ActiveModsFile();
            activeModsFile.write(string, ActiveMods.getById("default"));
        } catch (Exception exception) {
            ExceptionLogger.logException(exception);
        }
    }

    public void loadModPackFiles() {
        for (String string0 : this.mods) {
            try {
                ChooseGameInfo.Mod mod = ChooseGameInfo.getAvailableModDetails(string0);
                if (mod != null) {
                    for (ChooseGameInfo.PackFile packFile : mod.getPacks()) {
                        String string1 = this.getRelativeFile("media/texturepacks/" + packFile.name + ".pack");
                        string1 = string1.toLowerCase(Locale.ENGLISH);
                        if (!this.ActiveFileMap.containsKey(string1)) {
                            DebugLog.Mod.warn("pack file \"" + packFile.name + "\" needed by " + string0 + " not found");
                        } else {
                            String string2 = instance.getString("media/texturepacks/" + packFile.name + ".pack");
                            if (!this.LoadedPacks.contains(string2)) {
                                zombie.GameWindow.LoadTexturePack(packFile.name, packFile.flags, string0);
                                this.LoadedPacks.add(string2);
                            }
                        }
                    }
                }
            } catch (Exception exception) {
                ExceptionLogger.logException(exception);
            }
        }

        zombie.GameWindow.setTexturePackLookup();
    }

    public void loadModTileDefs() {
        HashSet hashSet = new HashSet();

        for (String string0 : this.mods) {
            try {
                ChooseGameInfo.Mod mod = ChooseGameInfo.getAvailableModDetails(string0);
                if (mod != null) {
                    for (ChooseGameInfo.TileDef tileDef : mod.getTileDefs()) {
                        if (hashSet.contains(tileDef.fileNumber)) {
                            DebugLog.Mod.error("tiledef fileNumber " + tileDef.fileNumber + " used by more than one mod");
                        } else {
                            String string1 = tileDef.name;
                            String string2 = this.getRelativeFile("media/" + string1 + ".tiles");
                            string2 = string2.toLowerCase(Locale.ENGLISH);
                            if (!this.ActiveFileMap.containsKey(string2)) {
                                DebugLog.Mod.error("tiledef file \"" + tileDef.name + "\" needed by " + string0 + " not found");
                            } else {
                                string1 = (String)this.ActiveFileMap.get(string2);
                                IsoWorld.instance.LoadTileDefinitions(IsoSpriteManager.instance, string1, tileDef.fileNumber);
                                hashSet.add(tileDef.fileNumber);
                            }
                        }
                    }
                }
            } catch (Exception exception) {
                exception.printStackTrace();
            }
        }
    }

    public void loadModTileDefPropertyStrings() {
        HashSet hashSet = new HashSet();

        for (String string0 : this.mods) {
            try {
                ChooseGameInfo.Mod mod = ChooseGameInfo.getAvailableModDetails(string0);
                if (mod != null) {
                    for (ChooseGameInfo.TileDef tileDef : mod.getTileDefs()) {
                        if (hashSet.contains(tileDef.fileNumber)) {
                            DebugLog.Mod.error("tiledef fileNumber " + tileDef.fileNumber + " used by more than one mod");
                        } else {
                            String string1 = tileDef.name;
                            String string2 = this.getRelativeFile("media/" + string1 + ".tiles");
                            string2 = string2.toLowerCase(Locale.ENGLISH);
                            if (!this.ActiveFileMap.containsKey(string2)) {
                                DebugLog.Mod.error("tiledef file \"" + tileDef.name + "\" needed by " + string0 + " not found");
                            } else {
                                string1 = (String)this.ActiveFileMap.get(string2);
                                IsoWorld.instance.LoadTileDefinitionsPropertyStrings(IsoSpriteManager.instance, string1, tileDef.fileNumber);
                                hashSet.add(tileDef.fileNumber);
                            }
                        }
                    }
                }
            } catch (Exception exception) {
                exception.printStackTrace();
            }
        }
    }

    public void loadFileGuidTable() {
        File file = instance.getMediaFile("fileGuidTable.xml");

        try {
            FileInputStream fileInputStream0 = new FileInputStream(file);

            try {
                JAXBContext jAXBContext0 = JAXBContext.newInstance(zombie.FileGuidTable.class);
                Unmarshaller unmarshaller0 = jAXBContext0.createUnmarshaller();
                this.m_fileGuidTable = (zombie.FileGuidTable)unmarshaller0.unmarshal(fileInputStream0);
                this.m_fileGuidTable.setModID("game");
            } catch (Throwable throwable0) {
                try {
                    fileInputStream0.close();
                } catch (Throwable throwable1) {
                    throwable0.addSuppressed(throwable1);
                }

                throw throwable0;
            }

            fileInputStream0.close();
        } catch (IOException | JAXBException jAXBException) {
            System.err.println("Failed to load file Guid table.");
            ExceptionLogger.logException(jAXBException);
            return;
        }

        try {
            JAXBContext jAXBContext1 = JAXBContext.newInstance(zombie.FileGuidTable.class);
            Unmarshaller unmarshaller1 = jAXBContext1.createUnmarshaller();

            for (String string : this.getModIDs()) {
                ChooseGameInfo.Mod mod = ChooseGameInfo.getAvailableModDetails(string);
                if (mod != null) {
                    try {
                        FileInputStream fileInputStream1 = new FileInputStream(this.getModDir(string) + "/media/fileGuidTable.xml");

                        try {
                            zombie.FileGuidTable fileGuidTable = (zombie.FileGuidTable)unmarshaller1.unmarshal(fileInputStream1);
                            fileGuidTable.setModID(string);
                            this.m_fileGuidTable.mergeFrom(fileGuidTable);
                        } catch (Throwable throwable2) {
                            try {
                                fileInputStream1.close();
                            } catch (Throwable throwable3) {
                                throwable2.addSuppressed(throwable3);
                            }

                            throw throwable2;
                        }

                        fileInputStream1.close();
                    } catch (FileNotFoundException fileNotFoundException) {
                    } catch (Exception exception0) {
                        ExceptionLogger.logException(exception0);
                    }
                }
            }
        } catch (Exception exception1) {
            ExceptionLogger.logException(exception1);
        }

        this.m_fileGuidTable.loaded();
        if (!this.m_fileGuidTableWatcherActive) {
            zombie.DebugFileWatcher.instance.add(new zombie.PredicatedFileWatcher("media/fileGuidTable.xml", var1x -> this.loadFileGuidTable()));
            this.m_fileGuidTableWatcherActive = true;
        }
    }

    public zombie.FileGuidTable getFileGuidTable() {
        if (this.m_fileGuidTable == null) {
            this.loadFileGuidTable();
        }

        return this.m_fileGuidTable;
    }

    public String getFilePathFromGuid(String guid) {
        zombie.FileGuidTable fileGuidTable = this.getFileGuidTable();
        return fileGuidTable != null ? fileGuidTable.getFilePathFromGuid(guid) : null;
    }

    public String getGuidFromFilePath(String path) {
        zombie.FileGuidTable fileGuidTable = this.getFileGuidTable();
        return fileGuidTable != null ? fileGuidTable.getGuidFromFilePath(path) : null;
    }

    public String resolveFileOrGUID(String source) {
        String string0 = source;
        String string1 = this.getFilePathFromGuid(source);
        if (string1 != null) {
            string0 = string1;
        }

        String string2 = string0.toLowerCase(Locale.ENGLISH);
        return this.ActiveFileMap.containsKey(string2) ? (String)this.ActiveFileMap.get(string2) : string0;
    }

    public boolean isValidFilePathGuid(String source) {
        return this.getFilePathFromGuid(source) != null;
    }

    public static File[] listAllDirectories(String rootPath, FileFilter filter, boolean recursive) {
        File file = new File(rootPath).getAbsoluteFile();
        return listAllDirectories(file, filter, recursive);
    }

    public static File[] listAllDirectories(File root, FileFilter filter, boolean recursive) {
        if (!root.isDirectory()) {
            return new File[0];
        } else {
            ArrayList arrayList = new ArrayList();
            listAllDirectoriesInternal(root, filter, recursive, arrayList);
            return (File[])arrayList.toArray(new File[0]);
        }
    }

    private static void listAllDirectoriesInternal(File file1, FileFilter fileFilter, boolean _boolean, ArrayList<File> arrayList) {
        File[] file0 = file1.listFiles();
        if (file0 != null) {
            for (File file2 : file0) {
                if (!file2.isFile() && file2.isDirectory()) {
                    if (fileFilter.accept(file2)) {
                        arrayList.add(file2);
                    }

                    if (_boolean) {
                        listAllFilesInternal(file2, fileFilter, true, arrayList);
                    }
                }
            }
        }
    }

    public static File[] listAllFiles(String folderPath, FileFilter filter, boolean recursive) {
        File file = new File(folderPath).getAbsoluteFile();
        return listAllFiles(file, filter, recursive);
    }

    public static File[] listAllFiles(File folder, FileFilter filter, boolean recursive) {
        if (!folder.isDirectory()) {
            return new File[0];
        } else {
            ArrayList arrayList = new ArrayList();
            listAllFilesInternal(folder, filter, recursive, arrayList);
            return (File[])arrayList.toArray(new File[0]);
        }
    }

    private static void listAllFilesInternal(File file1, FileFilter fileFilter, boolean _boolean, ArrayList<File> arrayList) {
        File[] file0 = file1.listFiles();
        if (file0 != null) {
            for (File file2 : file0) {
                if (file2.isFile()) {
                    if (fileFilter.accept(file2)) {
                        arrayList.add(file2);
                    }
                } else if (file2.isDirectory() && _boolean) {
                    listAllFilesInternal(file2, fileFilter, true, arrayList);
                }
            }
        }
    }

    public void walkGameAndModFiles(String relPath, boolean recursive, zombie.ZomboidFileSystem.IWalkFilesVisitor consumer) {
        this.walkGameAndModFilesInternal(this.base, relPath, recursive, consumer);
        ArrayList arrayList = this.getModIDs();

        for (int _int = 0; _int < arrayList.size(); _int++) {
            String string = this.getModDir((String)arrayList.get(_int));
            if (string != null) {
                this.walkGameAndModFilesInternal(new File(string), relPath, recursive, consumer);
            }
        }
    }

    private void walkGameAndModFilesInternal(File file1, String string, boolean _boolean, zombie.ZomboidFileSystem.IWalkFilesVisitor iWalkFilesVisitor) {
        File file0 = new File(file1, string);
        if (file0.isDirectory()) {
            File[] file2 = file0.listFiles();
            if (file2 != null) {
                for (File file3 : file2) {
                    iWalkFilesVisitor.visit(file3, string);
                    if (_boolean && file3.isDirectory()) {
                        this.walkGameAndModFilesInternal(file1, string + "/" + file3.getName(), true, iWalkFilesVisitor);
                    }
                }
            }
        }
    }

    public String[] resolveAllDirectories(String relPath, FileFilter filter, boolean recursive) {
        ArrayList arrayList = new ArrayList();
        this.walkGameAndModFiles(relPath, recursive, (file, string1) -> {
            if (file.isDirectory() && filter.accept(file)) {
                String string0 = string1 + "/" + file.getName();
                if (!arrayList.contains(string0)) {
                    arrayList.add(string0);
                }
            }
        });
        return (String[])arrayList.toArray(new String[0]);
    }

    public String[] resolveAllFiles(String relPath, FileFilter filter, boolean recursive) {
        ArrayList arrayList = new ArrayList();
        this.walkGameAndModFiles(relPath, recursive, (file, string1) -> {
            if (file.isFile() && filter.accept(file)) {
                String string0 = string1 + "/" + file.getName();
                if (!arrayList.contains(string0)) {
                    arrayList.add(string0);
                }
            }
        });
        return (String[])arrayList.toArray(new String[0]);
    }

    public String normalizeFolderPath(String path) {
        path = path.toLowerCase(Locale.ENGLISH).replace('\\', '/');
        path = path + "/";
        return path.replace("///", "/").replace("//", "/");
    }

    public static String processFilePath(String filePath, char separatorChar) {
        if (separatorChar != '\\') {
            filePath = filePath.replace('\\', separatorChar);
        }

        if (separatorChar != '/') {
            filePath = filePath.replace('/', separatorChar);
        }

        return filePath;
    }

    public boolean tryDeleteFile(String filePath) {
        if (StringUtils.isNullOrWhitespace(filePath)) {
            return false;
        } else {
            try {
                return this.deleteFile(filePath);
            } catch (AccessControlException | IOException iOException) {
                ExceptionLogger.logException(iOException, String.format("Failed to delete file: \"%s\"", filePath), DebugLog.FileIO, LogSeverity.General);
                return false;
            }
        }
    }

    public boolean deleteFile(String filePath) throws IOException {
        File file = new File(filePath).getAbsoluteFile();
        if (!file.isFile()) {
            throw new FileNotFoundException(String.format("File path not found: \"%s\"", filePath));
        } else if (file.delete()) {
            DebugLog.FileIO.debugln("File deleted successfully: \"%s\"", filePath);
            return true;
        } else {
            DebugLog.FileIO.debugln("Failed to delete file: \"%s\"", filePath);
            return false;
        }
    }

    public void update() {
        if (this.m_modsChangedTime != 0L) {
            long _long = System.currentTimeMillis();
            if (this.m_modsChangedTime <= _long) {
                this.m_modsChangedTime = 0L;
                this.modFolders = null;
                this.modIdToDir.clear();
                this.modDirToMod.clear();
                ChooseGameInfo.Reset();

                for (String string : this.getModIDs()) {
                    ChooseGameInfo.getModDetails(string);
                }

                LuaEventManager.triggerEvent("OnModsModified");
            }
        }
    }

    private boolean isModFile(String string0) {
        if (this.m_modsChangedTime > 0L) {
            return false;
        } else if (this.modFolders == null) {
            return false;
        } else {
            string0 = string0.toLowerCase().replace('\\', '/');
            if (string0.endsWith("/mods/default.txt")) {
                return false;
            } else {
                for (int _int = 0; _int < this.modFolders.size(); _int++) {
                    String string1 = ((String)this.modFolders.get(_int)).toLowerCase().replace('\\', '/');
                    if (string0.startsWith(string1)) {
                        return true;
                    }
                }

                return false;
            }
        }
    }

    private void onModFileChanged(String var1) {
        this.m_modsChangedTime = System.currentTimeMillis() + 2000L;
    }

    public void cleanMultiplayerSaves() {
        DebugLog.FileIO.println("Start cleaning save fs");
        String string0 = this.getSaveDir();
        String string1 = string0 + File.separator + "Multiplayer" + File.separator;
        File file0 = new File(string1);
        if (!file0.exists()) {
            file0.mkdir();
        }

        try {
            File[] file1 = file0.listFiles();

            for (File file2 : file1) {
                DebugLog.FileIO.println("Checking " + file2.getAbsoluteFile() + " dir");
                if (file2.isDirectory()) {
                    File file3 = new File(file2.toString() + File.separator + "map.bin");
                    if (file3.exists()) {
                        DebugLog.FileIO.println("Processing " + file2.getAbsoluteFile() + " dir");

                        try {
                            Stream stream = Files.walk(file2.toPath());
                            stream.forEach(path -> {
                                if (path.getFileName().toString().matches("map_\\d+_\\d+.bin")) {
                                    DebugLog.FileIO.println("Delete " + path.getFileName().toString());
                                    path.toFile().delete();
                                }
                            });
                        } catch (IOException iOException) {
                            throw new RuntimeException(iOException);
                        }
                    }
                }
            }
        } catch (RuntimeException runtimeException) {
            runtimeException.printStackTrace();
        }
    }

    public void resetDefaultModsForNewRelease(String versionStr) {
        ensureFolderExists(this.getCacheDirSub("mods"));
        String string0 = this.getCacheDirSub("mods") + File.separator + "reset-mods-" + versionStr + ".txt";
        File file = new File(string0);
        if (!file.exists()) {
            try {
                FileWriter fileWriter = new FileWriter(file);

                try {
                    BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);

                    try {
                        String string1 = "If this file does not exist, default.txt will be reset to empty (no mods active).";
                        bufferedWriter.write(string1);
                    } catch (Throwable throwable0) {
                        try {
                            bufferedWriter.close();
                        } catch (Throwable throwable1) {
                            throwable0.addSuppressed(throwable1);
                        }

                        throw throwable0;
                    }

                    bufferedWriter.close();
                } catch (Throwable throwable2) {
                    try {
                        fileWriter.close();
                    } catch (Throwable throwable3) {
                        throwable2.addSuppressed(throwable3);
                    }

                    throw throwable2;
                }

                fileWriter.close();
            } catch (Exception exception) {
                ExceptionLogger.logException(exception);
                return;
            }

            ActiveMods activeMods = ActiveMods.getById("default");
            activeMods.clear();
            this.saveModsFile();
        }
    }

    public interface IWalkFilesVisitor {
        void visit(File file, String relPath);
    }
}
